#include "zq_app.h"
#include "wifiscan.h"
#include "ql_wifi_scan.h"

#if (ZQ_COMM_PLATFORM == 8910) // 8910 platform start BLE Broadcast
#include "ql_api_bt.h"
#include "ql_api_ble_gatt.h"
#include "ql_api_osi.h"
#endif

ql_task_t wifiscan_task;
static ql_mutex_t WifiScanMutex;
// wifi scan global variables -----------------------------------------------------------------------------------------------
static ZQ_AP_INFO zq_ApInfos[ZQ_WIFI_AP_SCAN_CNT] = {0};
static int zq_ApCnt = -1;
static ZQ_AP_INFO_LIB zq_aps_lib[ZQ_WIFI_AP_LIB_SZIE] = { 0 }; // a lib saving all exist aps for user selecting stable ap.
// ApNum和ApInfo必须一起获得, 否则可能被任务切换导致修改不一致
int zq_GetApInfo(char *buf, int sz)
{
	int ret;
	buf[0] = '\0';
	if (sz < ZQ_AP_INFO_BUFLEN) return 0;
	ql_rtos_mutex_lock(WifiScanMutex, 100);
	ret = (zq_ApCnt <= 0) ? 0 : zq_ApCnt;
	for(int i = 0; i < zq_ApCnt; i++){
		// Modified by Conan -- 2023-02-22 16:18 -- 去掉SSID部分，服务器似乎处理这个部分有问题。怀疑是加了导致服务器有几率认为包超长，或者中文导致，会丢弃
		// sprintf(buf, "%s,%d,%s|", zq_ApInfos[i].mac, zq_ApInfos[i].rssi, zq_ApInfos[i].ssid);
		sprintf(buf, "%s,%d|", zq_ApInfos[i].mac, zq_ApInfos[i].rssi);
		buf += strlen(buf);
	}
	// remove the last '|'
	if(zq_ApCnt > 0) *(buf - 1) = '\0';
	ql_rtos_mutex_unlock(WifiScanMutex);
	return ret;
}
int zq_GetApNum(void)
{
	int ret;
	ql_rtos_mutex_lock(WifiScanMutex, 100);
	ret = (zq_ApCnt <= 0) ? 0 : zq_ApCnt;
	ql_rtos_mutex_unlock(WifiScanMutex);
	return ret;
}

static uint16_t ZqWifiScan(int max_cnt, ql_wifi_ap_info_s ApInfos[])
{
	int ret = 0;
	uint16_t cnt = 0;
	//complete flow to do one time scan: option->open->scan->close->update lib
	// reset parameters
	ZQLOG("ql_wifiscan_option_set: %d, %d, %d, %d", max_cnt, ZQ_WIFI_SCAN_ROUND, ZQ_WIFI_SCAN_CHANNEL_ALL, QL_WIFI_SCAN_DEFAULT_TIME);
	if (QL_WIFISCAN_SUCCESS != ql_wifiscan_option_set(max_cnt, ZQ_WIFI_SCAN_ROUND, ZQ_WIFI_SCAN_CHANNEL_ALL, QL_WIFI_SCAN_DEFAULT_TIME))
	{
		ZQLOG("func-ql_wifiscan_option_set failed");
		return cnt;
	}
	// open scan; kg modify 20231026 这个函数容易发生错误，特别是QL_WIFISCAN_HW_OCCUPIED_ERR错误, 延时多次尝试
	for(int i = 0; i < 5; i++, ql_rtos_task_sleep_s(10)) { 
		ret = ql_wifiscan_open();
		ZQLOG("Get wifi scan infomation, try ql_wifiscan_open %d time; err code: %d", i+1, ret);
		if(ret == QL_WIFISCAN_SUCCESS) break;	
	}
	if (ret != QL_WIFISCAN_SUCCESS)
	{
		ZQLOG("func-ql_wifiscan_open failed: %d", ret);
		return cnt;
	}
	ZQLOG("begin call ql_wifiscan_do");
	// begin scan
	if (QL_WIFISCAN_SUCCESS != ql_wifiscan_do(&cnt, ApInfos))
	{
		cnt = 0;
		ZQLOG("func-ql_wifiscan_do failed");
		return cnt;
	}
	ZQLOG("end ql_wifiscan_do(&cnt, ApInfos): cnt: %d", cnt);
	// close
	if (QL_WIFISCAN_SUCCESS != ql_wifiscan_close())
	{
		ZQLOG("func-ql_wifiscan_close failed");
		return cnt;
	}
	// 把新scan到的AP数据（ApInfos）记录到数据库（zq_aps_lib）
	for (int i = 0; i < cnt; i++) {
		int j = 0, pos = ZQ_WIFI_AP_LIB_SZIE;
		for (; j < ZQ_WIFI_AP_LIB_SZIE; j++) { // 逐一和库中数据比对
			if (zq_aps_lib[j].rec_idx == 0 && pos == ZQ_WIFI_AP_LIB_SZIE) { // search an empty pos, record the first pos for the new data
				pos = j;
			}
			else if (zq_aps_lib[j].bssid_low == ApInfos[i].bssid_low && zq_aps_lib[j].bssid_high == ApInfos[i].bssid_high) { // SCAN到相同AP
				if (ApInfos[i].rssival < -70) break; // scan到信号强度低，不推荐
				else if (zq_aps_lib[j].rssival > 0) break;  // 实测单次scan中有重复出现的AP，已经记录过，过滤掉
				else if ((ApInfos[i].rssival - zq_aps_lib[j].rssival) > 10
					|| (ApInfos[i].rssival - zq_aps_lib[j].rssival) < -10) {// scan到信号强度变化剧烈，不稳定，负向推荐
					if (zq_aps_lib[j].rec_idx > 0) zq_aps_lib[j].rec_idx--; // 推荐度 -1
				}
				else if (zq_aps_lib[j].rec_idx < 255) zq_aps_lib[j].rec_idx+=5; // 推荐度 +1 -- 实测ap数量较多时候，每次扫描得到的AP差异较大，所以增加推荐值，防止过快被清零
				zq_aps_lib[j].rssival = -ApInfos[i].rssival;  // 更新rssi，暂时记录为正数，表示更新过
				ApInfos[i].channel = zq_aps_lib[j].rec_idx; // 暂时把推荐值记录到不用的channel栏位，供后面排序用
				break;
			}
		}
		if ((j == ZQ_WIFI_AP_LIB_SZIE) && (pos != ZQ_WIFI_AP_LIB_SZIE)) { // 没在库中搜索到，并且库中有空位置，新增
			zq_aps_lib[pos].bssid_low = ApInfos[i].bssid_low;
			zq_aps_lib[pos].bssid_high = ApInfos[i].bssid_high;
			zq_aps_lib[pos].rssival = -ApInfos[i].rssival;
			zq_aps_lib[pos].rec_idx = 5;
			ApInfos[i].channel = zq_aps_lib[pos].rec_idx; // 暂时把推荐值记录到不用的channel栏位，供后面排序用
		}
	}
	for (int i = 0; i < ZQ_WIFI_AP_LIB_SZIE; i++) { // 降低库中没有scan到的AP的推荐值
		if (zq_aps_lib[i].rssival <= 0) {
			if (zq_aps_lib[i].rec_idx > 0) zq_aps_lib[i].rec_idx--; // 推荐度 -1
		}
		else zq_aps_lib[i].rssival = -zq_aps_lib[i].rssival;
	}
	return cnt;
}

// task function ----------------------------------------------------------------------------------
static void zq_WifiScanThread(void * arg)
{
	uint16_t cnt = 0;
	ql_wifi_ap_info_s ApInfos[ZQ_WIFI_AP_SCAN_CNT] = { 0 };
	ql_wifi_ap_info_s *pApInfos[ZQ_WIFI_AP_SCAN_CNT] = { 0 };

	// ql_rtos_task_sleep_s(5);
	// start a wifi base sync scan
	// 首次上电快速预扫描2次，建立AP数据库
	ZQLOG("Begin building ap database");
	for(int i = 0; i < 2; i++){
		memset(ApInfos, 0, sizeof(ApInfos));
		cnt = ZqWifiScan(ZQ_WIFI_AP_SCAN_CNT, ApInfos);
		for (int i = 0; i < cnt; i++) {
			#if (ZQ_COMM_PLATFORM == 8850)
			ZQLOG("Wifi_Scan: Num: %d, MAC-HIGH: %04x, MAC-LOW: %08x, RSSI: %d, SSID: %s", i, ApInfos[i].bssid_high, ApInfos[i].bssid_low, ApInfos[i].rssival, ApInfos[i].ssid);
			#elif (ZQ_COMM_PLATFORM == 8910)
			ZQLOG("Wifi_Scan: Num: %d, MAC-HIGH: %04x, MAC-LOW: %08x, RSSI: %d", i, ApInfos[i].bssid_high, ApInfos[i].bssid_low, ApInfos[i].rssival);
			#endif
		}
		ql_rtos_task_sleep_s(2);
	}
	ZQLOG("Completed building ap database");
	while(1) {
		cnt = ZqWifiScan(ZQ_WIFI_AP_SCAN_CNT, ApInfos);

		// 对新scan到的AP数据根据数据库历史记录选择稳定优质的排序 - 根据1. 频繁度; 2. rssi强度 排序
		for (int i = 0; i < cnt; i++) { // 依靠指针排序，速度快递
			pApInfos[i] = &ApInfos[i];
		}
		for (int i = 0; i < cnt - 1; i++) { // 简单冒泡排序
			for (int j = 0; j < cnt - 1 - i; j++) {
				int k = j + 1;
				if (pApInfos[j]->channel < pApInfos[k]->channel) { // 根据推荐值比较
					ZQ_SWAP_P(pApInfos[j], pApInfos[k]);
				}
				else if (pApInfos[j]->channel == pApInfos[k]->channel) { // 推荐值相同，进一步根据rssi比较
					if (pApInfos[j]->rssival < pApInfos[k]->rssival) {
						ZQ_SWAP_P(pApInfos[j], pApInfos[k]);
					}
				}
			}
		}
		// 组装最终要传输的ap数据。拼接/Copy 数据 mac
		ql_rtos_mutex_lock(WifiScanMutex, 100);
		zq_ApCnt = (cnt > ZQ_WIFI_AP_TRANS_CNT)? ZQ_WIFI_AP_TRANS_CNT : cnt;
		for(int i = 0; i < zq_ApCnt; i++)
		{
			ql_wifi_ap_info_s *pinfo = pApInfos[i];
			zq_ApInfos[i].rssi = pinfo->rssival;
			sprintf(zq_ApInfos[i].mac, "%02x:%02x:%02x:%02x:%02x:%02x", ((pinfo->bssid_high >> 8) & 0xFF), (pinfo->bssid_high & 0xFF),
				((unsigned int)(pinfo->bssid_low >> 24) & 0xFF), ((unsigned int)(pinfo->bssid_low >> 16) & 0xFF),
				((unsigned int)(pinfo->bssid_low >> 8) & 0xFF), ((unsigned int)pinfo->bssid_low & 0xFF));
			#if (ZQ_COMM_PLATFORM == 8850)
			strncpy(zq_ApInfos[i].ssid, (char *)pinfo->ssid, 32);
			#elif (ZQ_COMM_PLATFORM == 8910)
			zq_ApInfos[i].ssid[0] = 0;
			#endif
		}
		ql_rtos_mutex_unlock(WifiScanMutex);
		// LOG
		ZQLOG("Wifi_Scan: Count: %d", zq_ApCnt); 
		for(int i = 0; i < zq_ApCnt; i++) {
		    ZQLOG("Wifi_Scan: Num: %d, MAC: %s, RSSI: %d, SSID: %s", i, zq_ApInfos[i].mac, zq_ApInfos[i].rssi, zq_ApInfos[i].ssid);
		}

		// in wifi scan gap, start BLE broadcast
		// control next scan time
		if (zq_ApCnt > 0) // 上次scan正常，慢速scan
			ql_rtos_task_sleep_s(ZQ_WIFI_SCAN_PERIOD);
		else // 快速scan
			ql_rtos_task_sleep_s(10);

		// end BLE broadcast, prepare next wifi scan
	}
}

void ql_wifiscan_init(void)
{
   	QlOSStatus err;
	err = ql_rtos_mutex_create(&WifiScanMutex);
	err = ql_rtos_task_create(&wifiscan_task, 8 * 1024, APP_PRIORITY_NORMAL, "wifiscan", zq_WifiScanThread, NULL, 10);
	if(err != ZQ_RET_OK) ZQ_LOG_FAILED(ql_wifiscan_init, err);
}